//ENB Z 2.0.000 shaderfile
//ENB Z author: Marty McFly

/*

		 _______ _________ _______  _______  _ 
		(  ____ \\__   __/(  ___  )(  ____ )( )
		| (    \/   ) (   | (   ) || (    )|| |
		| (_____    | |   | |   | || (____)|| |
		(_____  )   | |   | |   | ||  _____)| |
		      ) |   | |   | |   | || (      (_)
		/\____) |   | |   | (___) || |       _ 
		\_______)   )_(   (_______)|_)      (_)


	Before you change anything here, please notice that you
	are allowed to modify my ENB config ONLY for yourself!

	Please read my agreement for more informations!
		- http://pastebin.com/Y6AEFvdS

	Only change something here if you know exactly what 
	you are doing! I'm not responsible for any crashes,
	malfunctions, thermonuclear disasters etc.

	Copyright:
	Boris Vorontsov, Rockstar Games, Timothy Lottes, iCE La GlacE,
	Cody Darr (Sonic Ether), Marty McFly

*/

#include "ENBZ_config.ini"	//include our config file

/////////////////////////TEXTURES///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////TEXTURES///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

float4 ScreenSize;
float4 Timer;
texture texColor;
texture texDepth;
texture texNoise;

sampler gcolor = sampler_state
{
    	Texture = (texColor);
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

sampler gdepth = sampler_state
{
	Texture   = <texDepth>;
};

sampler gnoise = sampler_state
{
    	Texture = (texNoise);
    	MinFilter = LINEAR;
	MagFilter = LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

/////////////////////////VERTEX SHADER//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////VERTEX SHADER//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void VS_ENBZ(inout float2 txcoord : TEXCOORD0, out float4 vpos : POSITION, in float3 pos : POSITION)
{
	vpos=float4(pos.x,pos.y,pos.z,1.0);
}

/////////////////////////FUNCTIONS//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////FUNCTIONS//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Bloom(in float2 tex, in bool isvert, inout float alpha)
{
	float2 pixelsize = float2(ScreenSize.y,ScreenSize.y*ScreenSize.z);
	float weight[5] = {0.17467,0.12098,0.06559,0.02783,0.01222};
	alpha = tex2D(gcolor, tex).w * 0.19741;
	float2 axis = (isvert == 1) ? float2(0.0,1.0) : float2(1.0,0.0);
	for(int i=1; i<6; i++)
	{
		alpha += tex2Dlod(gcolor, float4(tex + (float)i * pixelsize.xy * axis.xy,0,0)).w * weight[i-1];
		alpha += tex2Dlod(gcolor, float4(tex - (float)i * pixelsize.xy * axis.xy,0,0)).w * weight[i-1];
	}
}

void FXAA2(inout float3 color, in float2 pos)
{
	#define FXAA_SUBPIX_SHIFT (1.0/8.0)
	#define FXAA_REDUCE_MIN   (1.0/32.0)
	#define FXAA_REDUCE_MUL   (1.0/16.0)
	#define FXAA_SPAN_MAX     8.0

	half2 rcpFrame = half2(1/ScreenSize.x, 1/(ScreenSize.x/ScreenSize.z));
	half4 posPos;
	posPos.xy = pos;
	posPos.zw = posPos.xy - (rcpFrame.xy * (0.5 + FXAA_SUBPIX_SHIFT));
	half3 rgbNW = tex2D(gcolor, posPos.zw ).xyz;
	half3 rgbNE = tex2D(gcolor, posPos.zw + half2(rcpFrame.x, 0.0) ).xyz;
	half3 rgbSW = tex2D(gcolor, posPos.zw + half2(0.0, rcpFrame.y) ).xyz;
	half3 rgbSE = tex2D(gcolor, posPos.zw +rcpFrame.xy ).xyz;
	half3 rgbM  = tex2D(gcolor, posPos.xy).xyz;
       	half3 luma = half3(0.299, 0.587, 0.114);
	half lumaNW = dot(rgbNW, luma);
	half lumaNE = dot(rgbNE, luma);
	half lumaSW = dot(rgbSW, luma);
	half lumaSE = dot(rgbSE, luma);
	half lumaM  = dot(rgbM,  luma);
       	half lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
	half lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
       	half2 dir; 
       	half lumaNWNE = lumaNW + lumaNE;
	half lumaSWSE = lumaSW + lumaSE;
	dir.x = -((lumaNWNE) - (lumaSWSE));
	dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));
       	half dirReduce = max( (lumaSWSE + lumaNWNE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
	half rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
	dir = min(half2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX), max(half2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * rcpFrame.xy;
       	half3 rgbA = (1.0/2.0) * (tex2D(gcolor, posPos.xy + dir * (1.0/3.0 - 0.5) ).xyz + tex2D(gcolor, posPos.xy + dir * (2.0/3.0 - 0.5) ).xyz);
	half3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (tex2D(gcolor, posPos.xy + dir * (0.0/3.0 - 0.5) ).xyz + tex2D(gcolor, posPos.xy + dir * (3.0/3.0 - 0.5) ).xyz);
	half lumaB = dot(rgbB, luma);
 	color.xyz = (lumaB < lumaMin || lumaB > lumaMax) ? rgbA.xyz : rgbB.xyz;
}

void Sharpen(inout float3 color, in float2 tex)
{
	float2 pixelsize = float2(ScreenSize.y,ScreenSize.y*ScreenSize.z);
	float offset_bias = 0.35;
	float sharp_strength_luma = 0.13;
	float sharp_clamp = 0.25;
	float3 ori = color.xyz;
    	float3 blur_ori = tex2D(gcolor, tex.xy + float2(0.5 * pixelsize.x,-pixelsize.y * offset_bias)).rgb;  // South South East
    	blur_ori += tex2D(gcolor, tex.xy + float2(offset_bias * -pixelsize.x,0.5 * -pixelsize.y)).rgb; // West South West
    	blur_ori += tex2D(gcolor, tex.xy + float2(offset_bias * pixelsize.x,0.5 * pixelsize.y)).rgb; // East North East
    	blur_ori += tex2D(gcolor, tex.xy + float2(0.5 * -pixelsize.x,pixelsize.y * offset_bias)).rgb; // North North West
	blur_ori /= 4.0; 
	float3 sharp = ori - blur_ori;
	float sharp_luma = dot(sharp, sharp_strength_luma);	
	sharp_luma = clamp(sharp_luma, -sharp_clamp, sharp_clamp);
	color.xyz += sharp_luma;
}

void Grain (inout float3 color, in float2 texcoord)
{
	float2 seed1 = texcoord.xy*14.3+0.42;
	float2 seed2 = texcoord.xy*14.0+0.25;
	float timefact = fmod(Timer.x*35+cos(Timer.x*1.2),1);
	seed1.xy+=timefact*10;
	seed2.xy-=timefact*10;
	float4 Noise=(tex2D(gnoise,seed1)*2-1)*(tex2D(gnoise,seed2)*2-1);
	float NoiseAmp = 1.1;
	float colorgray = dot(color.xyz,float3(0.299,0.587,0.114));
	float noiseDown = smoothstep(0.2,0.0,colorgray);
	noiseDown += colorgray;
	float finalnoise = lerp(Noise,0.0,pow(noiseDown,4.0));
	color.xyz = pow(pow(color.xyz,1/NoiseAmp)+Noise*0.0333,NoiseAmp);
	color.xyz += finalnoise*0.04;
}

/////////////////////////PIXEL SHADERS//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////PIXEL SHADERS//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


float4 PS_ENBZ1(float2 _v0 : TEXCOORD0) : COLOR
{
	float4 color = tex2D(gcolor, _v0.xy);
	#if(FXAA >= 1)
	 	FXAA2(color.xyz, _v0.xy);
	#endif
	color.w = 0;
	//these steps prevent hue shift - RGB 0.0 0.5 1.0 minus 0.5 is 0.0 0.0 0.5 which is a totally different color
	float3 redsample = tex2D(gcolor, _v0.xy*16.0-2.0).xyz;
	float3 greensample = tex2D(gcolor, _v0.xy*16.0-4.0).xyz;
	float3 bluesample = tex2D(gcolor, _v0.xy*16.0-6.0).xyz;
	float redgray = dot(redsample.xyz,0.333);
	float greengray = dot(greensample.xyz,0.333);
	float bluegray = dot(bluesample.xyz,0.333);
	redsample *= smoothstep(0.4,1.0,redgray);
	greensample *= smoothstep(0.4,1.0,greengray);
	bluesample *= smoothstep(0.4,1.0,bluegray);
	if(_v0.x > 0.125 && _v0.y > 0.125 && _v0.x < 0.1875 && _v0.y < 0.1875) color.w += redsample.x;
	if(_v0.x > 0.25  && _v0.y > 0.25  && _v0.x < 0.3125 && _v0.y < 0.3125) color.w += greensample.y;
	if(_v0.x > 0.375 && _v0.y > 0.375 && _v0.x < 0.4375 && _v0.y < 0.4375) color.w += bluesample.z;
	return color;
}

float4 PS_ENBZ2(float2 _v0 : TEXCOORD0) : COLOR
{

	float3 r1 = pow(tex2Dlod(gdepth,float4(_v0.xy,0,0)).x,0.33); 
	r1.xy = smoothstep(fDepthOfFieldMin,fDepthOfFieldMax,r1.z)*fDepthOfFieldRadius*float2(ScreenSize.y,ScreenSize.y*ScreenSize.z);
	#if(UI_MASK == 1 && DEPTHOFFIELD == 1)
		bool isHUDorMenu = 0;
		float2 	minimapcenter = float2(fMinimapCenter_X, fMinimapCenter_Y);		
		float 	minimapradius = 0.1;
		float2 minimapdist = _v0.xy-minimapcenter;
		minimapdist.x*=ScreenSize.z;
		if(length(minimapdist.xy)<fMinimapRadius) isHUDorMenu = 1;
		float checkdepth = 0;
		for(float x = 1; x <= 3; x+=1)
		{
			for(float y = 1; y <= 3; y+=1)
			{	
				checkdepth += tex2Dlod(gdepth,float4(float2(x,y)*0.25,0,0)).x;
			}
		}
		if(checkdepth == 9.0) isHUDorMenu = 1;
		float4 HUDedges = float4(fHUDLeft,fHUDRight,fHUDTop,fHUDBottom);
		if(_v0.x > HUDedges.x && _v0.x < HUDedges.y && _v0.y > HUDedges.z && _v0.y < HUDedges.w) isHUDorMenu = 1;
		if(isHUDorMenu != 0) r1.xy = 0.0;
	#endif
	float2 poisson[19] = {
		float2(0.000,0.000),
		float2(0.000,1.000),
		float2(0.8860,0.500),
		float2(0.8860,-0.500),
		float2(0.0000,-1.000),
		float2(-0.8860,-0.500),
		float2(-0.8860,0.500),
		float2(0.000,2.000),
		float2(1.7720,1.000),
		float2(1.7720,-1.000),
		float2(0.0000,-2.000),
		float2(-1.7720,-1.000),
		float2(-1.7720,1.000),
		float2(0.8860,1.500),
		float2(1.772,0.000),
		float2(0.8860,-1.500),
		float2(-0.8860,-1.500),
		float2(-1.7720,0.000),
		float2(-0.8860,1.500) };

	float4 color = 0;
	int Taps = 19;
	#if(DEPTHOFFIELD != 1)
		Taps = 1;		
	#endif
	[unroll] //why? for the glory of satan, of course!
	for(int i = 0; i < Taps; i++)
	{
		float2 curroffset = _v0.xy + poisson[i].xy*r1.xy;
		float4 r2 = tex2Dlod(gcolor, float4(curroffset.xy, 0.0, 0.0));
		color.xyz += pow(r2.xyz,8.0);

	}
	color.xyz /= Taps;
	color.xyz = pow(color.xyz, 0.125);
	if(_v0.x < 0.5 && _v0.y < 0.5) Bloom(_v0.xy, 0, color.w);
	return color;
}

float4 PS_ENBZ3(float2 _v0 : TEXCOORD0) : COLOR
{
	float4 color = tex2D(gcolor, _v0.xy);
	#if(FXAA >= 2)
		FXAA2(color.xyz, _v0.xy);
	#endif
	if(_v0.x < 0.5 && _v0.y < 0.5) Bloom(_v0.xy, 1, color.w);
	return color;
}

float4 PS_ENBZ4(float2 _v0 : TEXCOORD0) : COLOR
{
	float4 color = tex2D(gcolor, _v0.xy);
	#if(FXAA == 3)
		 FXAA2(color.xyz, _v0.xy);
	#endif
	#if(FXAA >= 2)
		Sharpen(color.xyz, _v0.xy);		//FXAA blurs rather strong. The sharpen values are precisely configured, do not change.
	#endif
	float3 bloom = float3(tex2D(gcolor, _v0.xy/16+0.125).w,
			      tex2D(gcolor, _v0.xy/16+0.250).w,
			      tex2D(gcolor, _v0.xy/16+0.375).w);
	float gray=dot(color.xyz, 0.333);
	float3 satcolor=pow(color.xyz, 1.33);
	float satgray=dot(satcolor.xyz, 0.333);
	color.xyz=satcolor.xyz*gray/(satgray+0.0001);
	bloom.xyz = lerp(dot(bloom.xyz,0.333),bloom.xyz,1.5);
	color.xyz += 0.70*bloom.xyz*(1-color.xyz);
	#if(IMAGEGRAIN != 0)
		Grain(color.xyz, _v0.xy);
	#endif
	#if(VINTAGE != 0)
		color.r = color.r * 1.3 + 0.01;
		color.g = color.g * 1.2;
		color.b = color.b * 0.75 + 0.10;
	#endif
	color.xyz = saturate(color.xyz - pow(length(_v0.xy-0.5)*1.41, 4) * VIGNETTING);
	//final colormod stuff
	color.xyz = (color.xyz - dot(color.xyz, 0.333)) * SATURATION + dot(color.xyz, 0.333);
	color.xyz = (pow(color.xyz, GAMMA) - 0.5) * CONTRAST + 0.5;
	color.xyz *= EXPOSURE;

	return color;
}

/////////////////////////TECHNIQUES/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////TECHNIQUES/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

technique PostProcess 
{
	pass P0 
	{
		VertexShader = compile vs_2_0 VS_ENBZ();
		PixelShader  = compile ps_3_0 PS_ENBZ1();
	}
}

technique PostProcess2
{
	pass P0
	{
		VertexShader = compile vs_2_0 VS_ENBZ();
		PixelShader  = compile ps_3_0 PS_ENBZ2();
	}
}

technique PostProcess3
{
	pass P0
	{
		VertexShader = compile vs_2_0 VS_ENBZ();
		PixelShader  = compile ps_3_0 PS_ENBZ3();
	}
}

technique PostProcess4
{
	pass P0
	{
		VertexShader = compile vs_2_0 VS_ENBZ();
		PixelShader  = compile ps_3_0 PS_ENBZ4();
	}
}